package com.zhj.tree.hash;

import java.util.HashMap;
import java.util.Map;

/**
 * HashMap源码阅读
 */
public class HashMapSource {

    public static void main(String[] args) {
        /**
         * HashCode的计算方式：可以根据虚拟机参数 -XX:hashCode=2(永远返回1) 指定hash策略，默认采用策略6进行计算
         * 1. 通过随机数生成
         * 2. 通过object的内存地址和随机数生成
         * 3. 永远返回1 用于测试
         * 4. 返回一个全局递增的序列数
         * 5. 直接采用object的地址值
         * 6. 通过线程中的四个变量：stateX，stateY，stateZ，stateW，组合运算hashcode值，根据计算结果同步修改这四个值
         *
         * 下面调用构造函数，指定17为初始的容量，那么就会计算 threshold 阈值（2的n次幂）；输入17，那么初始的阈值就会是32
         * 下面进行添加元素时，通过 resize() 对数组进行初始化根据 threshold 进行创建数组，初始化时数组的长度就为阈值的值根据加载因子进行计算为24
         * 添加元素完成之后在判断数组的长度有没有超过阈值的长度，如果超过了就再进行扩容
         *
         * 为什么初始化时阈值的长度为2的n次幂？
         * 因为通过对象hash的二进制数与数组长度进行 & 运算时获得的索引能够均匀的进行分布，减少hash碰撞，避免形成链表的结构
         *
         * 链表的长度如果超过了8就会转换为红黑树，但是在转换红黑树时还需要优先判断数组的长度是否小于64，如果小于64需要优先进行扩容
         *
         * 链表的扩容？
         * 1.7 链表的扩容采用的头插法，会改变原有的链表顺序，多线程的情况下会导致循环链表的产生
         * 1.8 采用了尾插法进行扩容，就不会出现改变顺序的问题了
         *
         * 为什么会使用头插法？
         * 根据缓存时间局部性原则来看的话，最近被访问的元素，将来被访问的频率就越高，所以头插法将改变顺序的话，下次进行查询时可以减少遍历的次数，提高查询效率
         */
        Map<Object, String> map = new HashMap<>(2);
        map.put(new HashMapSource(), "zs1");
        map.put(new HashMapSource(), "zs2");
        map.put(new HashMapSource(), "zs3");
        map.put(new HashMapSource(), "zs4");
        map.put(new HashMapSource(), "zs5");
        map.put(new HashMapSource(), "zs6");
        map.put(new HashMapSource(), "zs7");
        map.put(new HashMapSource(), "zs8");
        map.put(new HashMapSource(), "zs9");
    }

}
